/*
   Author: mahaiyuan
   Date: 2021/9/5 10:16 下午
   Description:
*/
package zlog

import (
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"io/fs"
	"log"
	"os"
	"strings"
	"sync"
	"time"
)

var (
	SugaredLogger *zap.SugaredLogger
	logConfig     *LogConfig //日志配置
	mlock         sync.Mutex //互斥锁
)

type LogConfig struct {
	Level  string `yaml:"level"`  //日志级别 debug info warn error panic fatal, 默认为 info
	Stdout bool   `yaml:"stdout"` //是否输出到控制台，默认为false
	Path   string `yaml:"path"`   //日志输出路径 默认为当前项目/log目录
	Name   string `yaml:"name"`   //日志文件名称，默认为zap.log zap.log.wf
}

func InitLog(config LogConfig) *zap.SugaredLogger {
	logger, err := zap.NewProduction()
	if err != nil {
		log.Panicf("init zap log error.%v\n", err)
	}
	return logger.Sugar()
}

func newLogger() *zap.Logger {
	var zapCores []zapcore.Core
	logConfig := getLogConfig()
	// 开启输出到控制台
	if logConfig.Stdout {
		core := zapcore.NewCore(getEncoder(), zapcore.AddSync(os.Stdout), zap.LevelEnablerFunc(
			func(level zapcore.Level) bool {
				return level >= getLogLevel(logConfig.Level) && level >= zapcore.DebugLevel
			},
		))
		zapCores = append(zapCores, core)
	}

	logPath := "./log"
	if len(logConfig.Path) != 0 {
		logPath = logConfig.Path
	}
	if _, err := os.Stat(logPath); err != nil && os.IsNotExist(err) {
		// 创建日志目录
		if err := os.MkdirAll(logPath, fs.ModePerm); err != nil {
			Panicf("create log path error. path=%s, err=%v", logPath, err)
		}
	}

	// 普通日志
	logFile := "zap.log"
	if len(logConfig.Name) != 0 {
		logFile = logConfig.Name
	}
	infoFile, err := os.OpenFile(logPath+"/"+logFile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
	if err != nil {
		Panicf("open log file error. %v", err)
	}
	coreInfo := zapcore.NewCore(getEncoder(), zapcore.AddSync(infoFile), zap.LevelEnablerFunc(
		func(level zapcore.Level) bool {
			return level >= getLogLevel(logConfig.Level) && level <= zapcore.InfoLevel
		},
	))
	zapCores = append(zapCores, coreInfo)

	// 告警日志
	warnFile, err := os.OpenFile(logPath+"/"+logFile+".wf", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
	if err != nil {
		Panicf("open log file error. %v", err)
	}
	coreWarn := zapcore.NewCore(getEncoder(), zapcore.AddSync(warnFile), zap.LevelEnablerFunc(func(level zapcore.Level) bool {
		return level >= zapcore.WarnLevel
	}))
	zapCores = append(zapCores, coreWarn)

	core := zapcore.NewTee(zapCores...)
	return zap.New(core)
}

func getEncoder() zapcore.Encoder {
	encoderConfig := zapcore.EncoderConfig{
		TimeKey:       "time",
		LevelKey:      "level",
		NameKey:       "logger",
		CallerKey:     "caller",
		FunctionKey:   zapcore.OmitKey,
		MessageKey:    "msg",
		StacktraceKey: "stacktrace",
		LineEnding:    zapcore.DefaultLineEnding,
		EncodeLevel:   zapcore.LowercaseLevelEncoder,
		EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
			enc.AppendString(t.Format("2006-01-02 15:04:05.000000"))
		},
		EncodeDuration: zapcore.SecondsDurationEncoder,
		EncodeCaller:   zapcore.ShortCallerEncoder,
	}
	return zapcore.NewJSONEncoder(encoderConfig)
}

func getLogConfig() *LogConfig {
	if logConfig == nil {
		logConfig = &LogConfig{
			Level:  "info",
			Stdout: false,
			Path:   "./log",
			Name:   "zap.log",
		}
	}
	return logConfig
}

func getLogLevel(level string) zapcore.Level {
	upperLevel := strings.ToUpper(level)
	switch upperLevel {
	case "DEBUG":
		return zapcore.DebugLevel
	case "INFO":
		return zapcore.InfoLevel
	case "WARN":
		return zapcore.WarnLevel
	case "ERROR":
		return zapcore.ErrorLevel
	case "DPANIC":
		return zapcore.DPanicLevel
	case "PANIC":
		return zapcore.PanicLevel
	case "FATAL":
		return zapcore.FatalLevel
	default:
		return zapcore.InfoLevel
	}
}
